﻿''' <summary>
''' Defines an animation as a series of frames within a source image asset.
''' </summary>
''' <remarks>
''' The AnimatedSprite class is a sprite with multiple Frames. The Frames collection should have each frame added in order of animation sequence. 
''' The AnimationRate property can be used to determine how long it takes to play one complete cycle of the animation. In the example below, the 
''' four frames of animation take 1/4 of a second to complete, therefore the animation is playing at 16 frames per second.
''' </remarks>
Public Class AnimatedSprite
    Inherits Sprite

    Protected _WaitTime As Single

    Private _AnimationRate As Single = 0.5F
    ''' <summary>
    ''' Specifies the amount of time that it takes to complete one cycle of this animation.
    ''' For example, if there are 4 frames and AnimationRate = 1.0 then each frame is displayed
    ''' for 1/4 of a second.
    ''' </summary>
    ''' <value></value>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Public Property AnimationRate() As Single
        Get
            Return _AnimationRate
        End Get
        Set(ByVal value As Single)
            _AnimationRate = value
        End Set
    End Property

    Private _FrameIndex As Integer
    ''' <summary>
    ''' Gets or sets the current frame index for this animation. The current frame
    ''' is the one which will be drawn on the next game loop iteration.
    ''' </summary>
    ''' <value></value>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Public Property FrameIndex() As Integer
        Get
            Return _FrameIndex
        End Get
        Set(ByVal value As Integer)
            If value > -1 AndAlso value < _Frames.Count - 1 Then
                _FrameIndex = value
            End If
        End Set
    End Property

    ''' <summary>
    ''' Gets the Rectangle bounds of the current frame.
    ''' </summary>
    ''' <value></value>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Public Overrides ReadOnly Property FrameBounds As System.Drawing.Rectangle
        Get
            Return _Frames(_FrameIndex)
        End Get
    End Property

    Protected _Frames As New List(Of Rectangle)
    ''' <summary>
    ''' The list of frame bounds (Rectangles) which make up this animation.
    ''' </summary>
    ''' <value></value>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Public ReadOnly Property Frames As List(Of Rectangle)
        Get
            Return _Frames
        End Get
    End Property

    Protected Friend Overrides Sub OnDraw(ByVal e As SpriteDrawEventArgs)
        MyBase.OnDraw(e)
    End Sub

    Protected Friend Overridable Sub OnUpdate(ByVal e As GameEngineEventArgs)
        _FrameBounds = _Frames(_FrameIndex)
        _WaitTime += e.Time.LastFrame
        If _WaitTime > (_AnimationRate / _Frames.Count) Then
            _WaitTime = 0.0F
            If _FrameIndex < _Frames.Count - 1 Then
                _FrameIndex += 1
            Else
                _FrameIndex = 0
            End If
        End If
    End Sub

    ''' <summary>
    ''' This method is not implemented on an AnimatedSprite and throws a NotImplementedExcpetion.
    ''' Add rectangles to the Frames collection instead.
    ''' </summary>
    ''' <param name="bounds"></param>
    ''' <remarks></remarks>
    Public Overrides Sub SetFrameBounds(ByVal bounds As System.Drawing.Rectangle)
        Throw New NotImplementedException("It is not valid to set the frame bounds on an animated sprite. Add to the Frames collection instead.")
    End Sub

    ''' <summary>
    ''' Adds multiple rectangles to the Frames collection using a grid pattern moving across columns and then down rows.
    ''' </summary>
    ''' <param name="rowCount">The number of rows of frames in the image.</param>
    ''' <param name="columnCount">The number of columns of frames in the image.</param>
    ''' <param name="frameWidth">The width of each frame rectangle.</param>
    ''' <param name="frameHeight">The height of each frame rectangle.</param>
    ''' <param name="skipRows">The number of rows to skip before capturing frames.</param>
    ''' <param name="skipColumns">The number of columns to skip before capturing frames.</param>
    ''' <remarks>
    ''' Use this method to quickly add multiple frames for an animation when the animation's image contains equally sized
    ''' frames, arranged in a grid pattern. This method will add frames from left to right and then top to bottom.
    ''' </remarks>
    Public Sub AddFramesHV(rowCount As Integer, columnCount As Integer, frameWidth As Single, frameHeight As Single, Optional skipRows As Integer = 0, Optional skipColumns As Integer = 0)
        For y As Integer = skipRows To skipRows + rowCount - 1
            For x As Integer = skipColumns To skipColumns + columnCount - 1
                _Frames.Add(New Rectangle(x * frameWidth, y * frameHeight, frameWidth, frameHeight))
            Next
        Next
    End Sub

    ''' <summary>
    ''' Adds multiple rectangles to the Frames collection using a grid pattern moving down rows and then across columns.
    ''' </summary>
    ''' <param name="rowCount">The number of rows of frames in the image.</param>
    ''' <param name="columnCount">The number of columns of frames in the image.</param>
    ''' <param name="frameWidth">The width of each frame rectangle.</param>
    ''' <param name="frameHeight">The height of each frame rectangle.</param>
    ''' <param name="skipRows">The number of rows to skip before capturing frames.</param>
    ''' <param name="skipColumns">The number of columns to skip before capturing frames.</param>
    ''' <remarks>
    ''' Use this method to quickly add multiple frames for an animation when the animation's image contains equally sized
    ''' frames, arranged in a grid pattern. This method will add frames from top to bottom and then left to right.
    ''' </remarks>
    Public Sub AddFramesVH(rowCount As Integer, columnCount As Integer, frameWidth As Single, frameHeight As Single, Optional skipRows As Integer = 0, Optional skipColumns As Integer = 0)
        For x As Integer = skipRows To skipRows + rowCount - 1
            For y As Integer = skipColumns To skipColumns + columnCount - 1
                _Frames.Add(New Rectangle(x * frameWidth, y * frameHeight, frameWidth, frameHeight))
            Next
        Next
    End Sub
End Class
